/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.nisco.dms.shiro;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springside.modules.security.utils.Digests;

import com.nisco.dms.entity.Company;
import com.nisco.dms.entity.RolePermission;
import com.nisco.dms.entity.User;
import com.nisco.dms.entity.UserRole;
import com.nisco.dms.service.CompanyService;
import com.nisco.dms.service.UserRoleService;
import com.nisco.dms.service.UserService;
import com.nisco.dms.util.Encodes;

/**
 * 
 * @author joey
 * 
 */
public class ShiroDbRealm extends AuthorizingRealm {
	private static final int INTERATIONS = 1024;
	private static final int SALT_SIZE = 8;
	private static final String ALGORITHM = "SHA-1";

	protected UserService userService;

	protected UserRoleService userRoleService;

	protected CompanyService companyService;
	/**
	 * 给ShiroDbRealm提供编码信息，用于密码密码比对 描述
	 */
	public ShiroDbRealm() {
		super();
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(ALGORITHM);
		matcher.setHashIterations(INTERATIONS);

		setCredentialsMatcher(matcher);
	}

	/**
	 * 认证回调函数, 登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		/**
		 * CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken)
		 * authcToken;
		 * 
		 * String parm = token.getCaptcha(); String c =
		 * (String)SecurityUtils.getSubject().getSession()
		 * .getAttribute(SimpleCaptchaServlet.CAPTCHA_KEY); // 忽略大小写 if
		 * (!parm.equalsIgnoreCase(c)) { throw new
		 * IncorrectCaptchaException("验证码错误！"); }
		 **/
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		String userName = token.getUsername().split(":")[0];
		String companyId = token.getUsername().split(":")[1];
		User user = userService.get(userName);
		Company company = companyService.get(companyId);
		if (user != null) {
			if (user.getStrStatus().equals("disabled")) {
				throw new DisabledAccountException();
			}

			byte[] salt = Encodes.decodeHex(user.getStrSalt());

			ShiroUser shiroUser = new ShiroUser(user.getId(), user.getStrAccount(), user,company);
			return new SimpleAuthenticationInfo(shiroUser, user.getStrPassword(), ByteSource.Util.bytes(salt), getName());
		} else {
			return null;
		}

	}

	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.fromRealm(getName()).iterator().next();
		List<UserRole> userRoles = userRoleService.find(shiroUser.getId());
		shiroUser.getUser().setUserRoles(userRoles);

		if (!userRoles.isEmpty()) {
			SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
			for (UserRole userRole : userRoles) {
				// 基于Permission的权限信息
				//info.addStringPermissions(userRole.getRole().getPermissionList());
					List<String> list=new ArrayList<String>();
					List<RolePermission> rplist=userRole.getRole().getPermissionList();
					for (RolePermission rolePermission : rplist)
					{
						list.add(rolePermission.getStrPermission());
					}
					info.addStringPermissions(list);
			}
			return info;
		} else {
			return null;
		}
	}

	public static class HashPassword {
		public String salt;
		public String password;
	}

	public HashPassword encrypt(String plainText) {
		HashPassword result = new HashPassword();
		byte[] salt = Digests.generateSalt(SALT_SIZE);
		result.salt = Encodes.encodeHex(salt);

		byte[] hashPassword = Digests.sha1(plainText.getBytes(), salt, INTERATIONS);
		result.password = Encodes.encodeHex(hashPassword);
		return result;

	}

	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
		clearCachedAuthorizationInfo(principals);
	}

	/**
	 * 清除所有用户授权信息缓存.
	 */
	public void clearAllCachedAuthorizationInfo() {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	/**
	 * 设置 userRoleService 的值
	 * 
	 * @param userRoleService
	 */
	public void setUserRoleService(UserRoleService userRoleService) {
		this.userRoleService = userRoleService;
	}
	
	/**
	 * 设置 companyService 的值
	 * @param companyService
	 * @author Robin 2013-4-25 上午8:51:24
	 */
	public void setCompanyService(CompanyService companyService) {
		this.companyService = companyService;
	}

	/**
	 * 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息.
	 */
	public static class ShiroUser implements Serializable {

		private static final long serialVersionUID = -1748602382963711884L;
		public String id;
		public String loginName;
		public Company company; //当前登录公司别
		public User user;

		public ShiroUser() {

		}

		public ShiroUser(String id, String loginName, String name,Company company) {
			this.id = id;
			this.loginName = loginName;
			this.loginName = name;
			this.company = company;
		}

		/**
		 * 构造函数
		 * 
		 * @param id
		 * @param loginName
		 * @param email
		 * @param createTime
		 * @param status
		 */
		public ShiroUser(String id, String loginName, User user,Company company) {
			this.id = id;
			this.loginName = loginName;
			this.user = user;
			this.company = company;
		}

		/**
		 * 返回 id 的值
		 * 
		 * @return id
		 */
		public String getId() {
			return id;
		}

		/**
		 * 返回 loginName 的值
		 * 
		 * @return loginName
		 */
		public String getLoginName() {
			return loginName;
		}

		/**
		 * 返回 user 的值
		 * 
		 * @return user
		 */
		public User getUser() {
			return user;
		}

		/**
		 * 返回company
		 * @return
		 * @author Robin 2013-4-25 上午8:45:44
		 */
		public Company getCompany() {
			return company;
		}

		/**
		 * 设置company
		 * @param company
		 * @author Robin 2013-4-25 上午8:45:49
		 */
		public void setCompany(Company company) {
			this.company = company;
		}

		/**
		 * 本函数输出将作为默认的<shiro:principal/>输出.
		 */
		@Override
		public String toString() {
			return loginName;
		}
	}
}
